package client

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

// FAQEntry represents a FAQ item stored under a knowledge base.
type FAQEntry struct {
	ID                string    `json:"id"`
	ChunkID           string    `json:"chunk_id"`
	KnowledgeID       string    `json:"knowledge_id"`
	KnowledgeBaseID   string    `json:"knowledge_base_id"`
	TagID             string    `json:"tag_id"`
	IsEnabled         bool      `json:"is_enabled"`
	StandardQuestion  string    `json:"standard_question"`
	SimilarQuestions  []string  `json:"similar_questions"`
	NegativeQuestions []string  `json:"negative_questions"`
	Answers           []string  `json:"answers"`
	IndexMode         string    `json:"index_mode"`
	UpdatedAt         time.Time `json:"updated_at"`
	CreatedAt         time.Time `json:"created_at"`
	Score             float64   `json:"score,omitempty"`
	MatchType         string    `json:"match_type,omitempty"`
	ChunkType         string    `json:"chunk_type"`
}

// FAQEntryPayload is used to create or update a FAQ entry.
type FAQEntryPayload struct {
	StandardQuestion  string   `json:"standard_question"`
	SimilarQuestions  []string `json:"similar_questions,omitempty"`
	NegativeQuestions []string `json:"negative_questions,omitempty"`
	Answers           []string `json:"answers"`
	TagID             string   `json:"tag_id,omitempty"`
	IsEnabled         *bool    `json:"is_enabled,omitempty"`
}

// FAQBatchUpsertPayload represents the request body for batch import (append/replace).
type FAQBatchUpsertPayload struct {
	Entries     []FAQEntryPayload `json:"entries"`
	Mode        string            `json:"mode"`
	KnowledgeID string            `json:"knowledge_id,omitempty"`
}

// FAQEntryStatusBatchRequest toggles enable states in bulk.
type FAQEntryStatusBatchRequest struct {
	Updates map[string]bool `json:"updates"`
}

// FAQEntryTagBatchRequest updates tags in bulk.
type FAQEntryTagBatchRequest struct {
	Updates map[string]*string `json:"updates"`
}

// FAQDeleteRequest deletes entries in bulk.
type FAQDeleteRequest struct {
	IDs []string `json:"ids"`
}

// FAQSearchRequest represents the hybrid FAQ search request.
type FAQSearchRequest struct {
	QueryText       string  `json:"query_text"`
	VectorThreshold float64 `json:"vector_threshold"`
	MatchCount      int     `json:"match_count"`
}

// FAQEntriesPage contains paginated FAQ results.
type FAQEntriesPage struct {
	Total    int64      `json:"total"`
	Page     int        `json:"page"`
	PageSize int        `json:"page_size"`
	Entries  []FAQEntry `json:"data"`
}

// FAQEntriesResponse wraps the paginated FAQ response.
type FAQEntriesResponse struct {
	Success bool            `json:"success"`
	Data    *FAQEntriesPage `json:"data"`
	Message string          `json:"message,omitempty"`
	Code    string          `json:"code,omitempty"`
}

// FAQUpsertResponse wraps the asynchronous import response.
type FAQUpsertResponse struct {
	Success bool            `json:"success"`
	Data    *FAQTaskPayload `json:"data"`
	Message string          `json:"message,omitempty"`
	Code    string          `json:"code,omitempty"`
}

// FAQTaskPayload carries the task identifier for async imports.
type FAQTaskPayload struct {
	TaskID string `json:"task_id"`
}

// FAQSearchResponse wraps the hybrid FAQ search results.
type FAQSearchResponse struct {
	Success bool       `json:"success"`
	Data    []FAQEntry `json:"data"`
	Message string     `json:"message,omitempty"`
	Code    string     `json:"code,omitempty"`
}

// FAQEntryResponse wraps the single FAQ entry creation response.
type FAQEntryResponse struct {
	Success bool      `json:"success"`
	Data    *FAQEntry `json:"data"`
	Message string    `json:"message,omitempty"`
	Code    string    `json:"code,omitempty"`
}

type faqSimpleResponse struct {
	Success bool   `json:"success"`
	Message string `json:"message,omitempty"`
	Code    string `json:"code,omitempty"`
}

// ListFAQEntries returns paginated FAQ entries under a knowledge base.
func (c *Client) ListFAQEntries(ctx context.Context,
	knowledgeBaseID string, page, pageSize int, tagID string, keyword string,
) (*FAQEntriesPage, error) {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries", knowledgeBaseID)
	query := url.Values{}
	if page > 0 {
		query.Add("page", strconv.Itoa(page))
	}
	if pageSize > 0 {
		query.Add("page_size", strconv.Itoa(pageSize))
	}
	if tagID != "" {
		query.Add("tag_id", tagID)
	}
	if keyword != "" {
		query.Add("keyword", keyword)
	}

	resp, err := c.doRequest(ctx, http.MethodGet, path, nil, query)
	if err != nil {
		return nil, err
	}

	var response FAQEntriesResponse
	if err := parseResponse(resp, &response); err != nil {
		return nil, err
	}
	if response.Data == nil {
		return &FAQEntriesPage{}, nil
	}
	return response.Data, nil
}

// UpsertFAQEntries imports or appends FAQ entries asynchronously and returns the task ID.
func (c *Client) UpsertFAQEntries(ctx context.Context,
	knowledgeBaseID string, payload *FAQBatchUpsertPayload,
) (string, error) {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodPost, path, payload, nil)
	if err != nil {
		return "", err
	}

	var response FAQUpsertResponse
	if err := parseResponse(resp, &response); err != nil {
		return "", err
	}
	if response.Data == nil {
		return "", fmt.Errorf("missing task information in response")
	}
	return response.Data.TaskID, nil
}

// CreateFAQEntry creates a single FAQ entry synchronously.
func (c *Client) CreateFAQEntry(ctx context.Context,
	knowledgeBaseID string, payload *FAQEntryPayload,
) (*FAQEntry, error) {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entry", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodPost, path, payload, nil)
	if err != nil {
		return nil, err
	}

	var response FAQEntryResponse
	if err := parseResponse(resp, &response); err != nil {
		return nil, err
	}
	return response.Data, nil
}

// UpdateFAQEntry updates a single FAQ entry.
func (c *Client) UpdateFAQEntry(ctx context.Context,
	knowledgeBaseID, entryID string, payload *FAQEntryPayload,
) error {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries/%s", knowledgeBaseID, entryID)
	resp, err := c.doRequest(ctx, http.MethodPut, path, payload, nil)
	if err != nil {
		return err
	}

	var response faqSimpleResponse
	return parseResponse(resp, &response)
}

// UpdateFAQEntryStatusBatch enables/disables FAQ entries in bulk.
func (c *Client) UpdateFAQEntryStatusBatch(ctx context.Context,
	knowledgeBaseID string, updates map[string]bool,
) error {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries/status", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodPut, path, &FAQEntryStatusBatchRequest{Updates: updates}, nil)
	if err != nil {
		return err
	}

	var response faqSimpleResponse
	return parseResponse(resp, &response)
}

// UpdateFAQEntryTagBatch updates FAQ entry tags in bulk.
func (c *Client) UpdateFAQEntryTagBatch(ctx context.Context,
	knowledgeBaseID string, updates map[string]*string,
) error {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries/tags", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodPut, path, &FAQEntryTagBatchRequest{Updates: updates}, nil)
	if err != nil {
		return err
	}

	var response faqSimpleResponse
	return parseResponse(resp, &response)
}

// DeleteFAQEntries deletes FAQ entries in bulk.
func (c *Client) DeleteFAQEntries(ctx context.Context,
	knowledgeBaseID string, ids []string,
) error {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/entries", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodDelete, path, &FAQDeleteRequest{IDs: ids}, nil)
	if err != nil {
		return err
	}

	var response faqSimpleResponse
	return parseResponse(resp, &response)
}

// SearchFAQEntries performs hybrid FAQ search inside a knowledge base.
func (c *Client) SearchFAQEntries(ctx context.Context,
	knowledgeBaseID string, payload *FAQSearchRequest,
) ([]FAQEntry, error) {
	path := fmt.Sprintf("/api/v1/knowledge-bases/%s/faq/search", knowledgeBaseID)
	resp, err := c.doRequest(ctx, http.MethodPost, path, payload, nil)
	if err != nil {
		return nil, err
	}

	var response FAQSearchResponse
	if err := parseResponse(resp, &response); err != nil {
		return nil, err
	}

	return response.Data, nil
}
